﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace ObjectMapper.Benchmark
{
    class Program
    {
        public static readonly IMapExecutor[] Executors =
        {
            new ObjectMapperExecutor(),
            new EmitMapperExecutor(),
            new AutoMapperExecutor(),
            new BLToolkitExecutor(), 
        };

        private static readonly IList<ExecutionResult> results = new List<ExecutionResult>();
        private static readonly Stopwatch watch = new Stopwatch();

        static void Main(string[] args)
        {
            Initialize();
            ExecuteAdventureWorks();
            Console.WriteLine("Press enter to exit");
            Console.Read();
        }

        #region AdventureWorks

        private static void ExecuteAdventureWorks()
        {
            results.Clear();
            Console.WriteLine("Executing Performance Benchmark against the AdventureWorks sample database...");
            AdventureWorksResult.WriteConsoleHeader();
            using (var context = new DataModel.DatabaseContext())
            {
                ExecuteAdventureWorksMapping<DataModel.DatabaseLog, BusinessModel.DatabaseLog>(context.DatabaseLog.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ErrorLog, BusinessModel.ErrorLog>(context.ErrorLog.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Department, BusinessModel.Department>(context.Department.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Employee, BusinessModel.Employee>(context.Employee.ToArray());
                ExecuteAdventureWorksMapping<DataModel.EmployeeDepartmentHistory, BusinessModel.EmployeeDepartmentHistory>(context.EmployeeDepartmentHistory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.EmployeePayHistory, BusinessModel.EmployeePayHistory>(context.EmployeePayHistory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.JobCandidate, BusinessModel.JobCandidate>(context.JobCandidate.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Shift, BusinessModel.Shift>(context.Shift.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Address, BusinessModel.Address>(context.Address.ToArray());
                ExecuteAdventureWorksMapping<DataModel.AddressType, BusinessModel.AddressType>(context.AddressType.ToArray());
                ExecuteAdventureWorksMapping<DataModel.BusinessEntity, BusinessModel.BusinessEntity>(context.BusinessEntity.ToArray());
                ExecuteAdventureWorksMapping<DataModel.BusinessEntityAddress, BusinessModel.BusinessEntityAddress>(context.BusinessEntityAddress.ToArray());
                ExecuteAdventureWorksMapping<DataModel.BusinessEntityContact, BusinessModel.BusinessEntityContact>(context.BusinessEntityContact.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ContactType, BusinessModel.ContactType>(context.ContactType.ToArray());
                ExecuteAdventureWorksMapping<DataModel.CountryRegion, BusinessModel.CountryRegion>(context.CountryRegion.ToArray());
                ExecuteAdventureWorksMapping<DataModel.EmailAddress, BusinessModel.EmailAddress>(context.EmailAddress.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Password, BusinessModel.Password>(context.Password.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Person, BusinessModel.Person>(context.Person.ToArray());
                ExecuteAdventureWorksMapping<DataModel.PersonPhone, BusinessModel.PersonPhone>(context.PersonPhone.ToArray());
                ExecuteAdventureWorksMapping<DataModel.PhoneNumberType, BusinessModel.PhoneNumberType>(context.PhoneNumberType.ToArray());
                ExecuteAdventureWorksMapping<DataModel.StateProvince, BusinessModel.StateProvince>(context.StateProvince.ToArray());
                ExecuteAdventureWorksMapping<DataModel.BillOfMaterials, BusinessModel.BillOfMaterials>(context.BillOfMaterials.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Culture, BusinessModel.Culture>(context.Culture.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Illustration, BusinessModel.Illustration>(context.Illustration.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Location, BusinessModel.Location>(context.Location.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Product, BusinessModel.Product>(context.Product.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductCategory, BusinessModel.ProductCategory>(context.ProductCategory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductCostHistory, BusinessModel.ProductCostHistory>(context.ProductCostHistory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductDescription, BusinessModel.ProductDescription>(context.ProductDescription.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductInventory, BusinessModel.ProductInventory>(context.ProductInventory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductListPriceHistory, BusinessModel.ProductListPriceHistory>(context.ProductListPriceHistory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductModel, BusinessModel.ProductModel>(context.ProductModel.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductModelIllustration, BusinessModel.ProductModelIllustration>(context.ProductModelIllustration.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductModelProductDescriptionCulture, BusinessModel.ProductModelProductDescriptionCulture>(context.ProductModelProductDescriptionCulture.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductPhoto, BusinessModel.ProductPhoto>(context.ProductPhoto.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductProductPhoto, BusinessModel.ProductProductPhoto>(context.ProductProductPhoto.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductReview, BusinessModel.ProductReview>(context.ProductReview.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductSubcategory, BusinessModel.ProductSubcategory>(context.ProductSubcategory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ScrapReason, BusinessModel.ScrapReason>(context.ScrapReason.ToArray());
                ExecuteAdventureWorksMapping<DataModel.TransactionHistory, BusinessModel.TransactionHistory>(context.TransactionHistory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.TransactionHistoryArchive, BusinessModel.TransactionHistoryArchive>(context.TransactionHistoryArchive.ToArray());
                ExecuteAdventureWorksMapping<DataModel.UnitMeasure, BusinessModel.UnitMeasure>(context.UnitMeasure.ToArray());
                ExecuteAdventureWorksMapping<DataModel.WorkOrder, BusinessModel.WorkOrder>(context.WorkOrder.ToArray());
                ExecuteAdventureWorksMapping<DataModel.WorkOrderRouting, BusinessModel.WorkOrderRouting>(context.WorkOrderRouting.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ProductVendor, BusinessModel.ProductVendor>(context.ProductVendor.ToArray());
                ExecuteAdventureWorksMapping<DataModel.PurchaseOrderDetail, BusinessModel.PurchaseOrderDetail>(context.PurchaseOrderDetail.ToArray());
                ExecuteAdventureWorksMapping<DataModel.PurchaseOrderHeader, BusinessModel.PurchaseOrderHeader>(context.PurchaseOrderHeader.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ShipMethod, BusinessModel.ShipMethod>(context.ShipMethod.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Vendor, BusinessModel.Vendor>(context.Vendor.ToArray());
                ExecuteAdventureWorksMapping<DataModel.CountryRegionCurrency, BusinessModel.CountryRegionCurrency>(context.CountryRegionCurrency.ToArray());
                ExecuteAdventureWorksMapping<DataModel.CreditCard, BusinessModel.CreditCard>(context.CreditCard.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Currency, BusinessModel.Currency>(context.Currency.ToArray());
                ExecuteAdventureWorksMapping<DataModel.CurrencyRate, BusinessModel.CurrencyRate>(context.CurrencyRate.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Customer, BusinessModel.Customer>(context.Customer.ToArray());
                ExecuteAdventureWorksMapping<DataModel.PersonCreditCard, BusinessModel.PersonCreditCard>(context.PersonCreditCard.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SalesOrderDetail, BusinessModel.SalesOrderDetail>(context.SalesOrderDetail.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SalesOrderHeader, BusinessModel.SalesOrderHeader>(context.SalesOrderHeader.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SalesOrderHeaderSalesReason, BusinessModel.SalesOrderHeaderSalesReason>(context.SalesOrderHeaderSalesReason.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SalesPerson, BusinessModel.SalesPerson>(context.SalesPerson.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SalesPersonQuotaHistory, BusinessModel.SalesPersonQuotaHistory>(context.SalesPersonQuotaHistory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SalesReason, BusinessModel.SalesReason>(context.SalesReason.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SalesTaxRate, BusinessModel.SalesTaxRate>(context.SalesTaxRate.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SalesTerritory, BusinessModel.SalesTerritory>(context.SalesTerritory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SalesTerritoryHistory, BusinessModel.SalesTerritoryHistory>(context.SalesTerritoryHistory.ToArray());
                ExecuteAdventureWorksMapping<DataModel.ShoppingCartItem, BusinessModel.ShoppingCartItem>(context.ShoppingCartItem.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SpecialOffer, BusinessModel.SpecialOffer>(context.SpecialOffer.ToArray());
                ExecuteAdventureWorksMapping<DataModel.SpecialOfferProduct, BusinessModel.SpecialOfferProduct>(context.SpecialOfferProduct.ToArray());
                ExecuteAdventureWorksMapping<DataModel.Store, BusinessModel.Store>(context.Store.ToArray());
            }
            Console.WriteLine();
            CreateReport("AdventureWorks", AdventureWorksResult.WriteCsvHeader);
            Console.WriteLine();
        }

        private static void ExecuteAdventureWorksMapping<TSource, TTarget>(TSource[] entities)
        {
            var result = new AdventureWorksResult(typeof(TSource), typeof(TTarget), entities.Length);
            foreach (var executor in Executors)
            {
                executor.PreExecute<TSource, TTarget>();
                watch.Restart();
                executor.Execute<TSource, TTarget>(entities);
                watch.Stop();
                result.Durations.Add(executor.Name, watch.Elapsed);
                executor.PostExecute<TSource, TTarget>();
            }
            result.WriteConsoleRow();
            results.Add(result);
        }

        #endregion

        private static void CreateReport(string reportName, Action<StreamWriter> headerRender)
        {
            if (!Directory.Exists("Reports"))
            {
                Directory.CreateDirectory("Reports");
            }
            var fileName = string.Format("Reports/{0}.{1}.csv", reportName, DateTime.Now.ToString("yyyyMMddHHmmss"));
            Console.WriteLine("Creating report file {0} ......", fileName);
            using (var fileStream = File.Open(fileName, FileMode.CreateNew, FileAccess.Write))
            {
                using (var writer = new StreamWriter(fileStream))
                {
                    headerRender(writer);
                    foreach (var result in results)
                    {
                        result.WriteCsvRow(writer);
                    }
                    writer.Flush();
                }
            }
            Console.WriteLine("The report file {0} has been completed", fileName);
        }

        private static void Initialize()
        {
            foreach (var executor in Executors)
            {
                executor.Initialize();
            }
        }
    }
}
